import { BoundingSphere } from '../../Source/Cesium.js';
import { Cartesian3 } from '../../Source/Cesium.js';
import { Cartesian4 } from '../../Source/Cesium.js';
import { Ellipsoid } from '../../Source/Cesium.js';
import { Intersect } from '../../Source/Cesium.js';
import { Math as CesiumMath } from '../../Source/Cesium.js';
import { Matrix3 } from '../../Source/Cesium.js';
import { Occluder } from '../../Source/Cesium.js';
import { OrientedBoundingBox } from '../../Source/Cesium.js';
import { Plane } from '../../Source/Cesium.js';
import { Quaternion } from '../../Source/Cesium.js';
import { Rectangle } from '../../Source/Cesium.js';
import createPackableSpecs from '../createPackableSpecs.js';

describe('Core/OrientedBoundingBox', function() {

    var positions = [
        new Cartesian3(2.0, 0.0, 0.0),
        new Cartesian3(0.0, 3.0, 0.0),
        new Cartesian3(0.0, 0.0, 4.0),
        new Cartesian3(-2.0, 0.0, 0.0),
        new Cartesian3(0.0, -3.0, 0.0),
        new Cartesian3(0.0, 0.0, -4.0)
    ];

    function rotatePositions(positions, axis, angle) {
        var points = [];

        var quaternion = Quaternion.fromAxisAngle(axis, angle);
        var rotation = Matrix3.fromQuaternion(quaternion);

        for (var i = 0; i < positions.length; ++i) {
            points.push(Matrix3.multiplyByVector(rotation, positions[i], new Cartesian3()));
        }

        return {
            points : points,
            rotation : rotation
        };
    }

    function translatePositions(positions, translation) {
        var points = [];
        for (var i = 0; i < positions.length; ++i) {
            points.push(Cartesian3.add(translation, positions[i], new Cartesian3()));
        }

        return points;
    }

    it('constructor sets expected default values', function() {
        var box = new OrientedBoundingBox();
        expect(box.center).toEqual(Cartesian3.ZERO);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
    });

    it('fromPoints constructs empty box with undefined positions', function() {
        var box = OrientedBoundingBox.fromPoints(undefined);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints constructs empty box with empty positions', function() {
        var box = OrientedBoundingBox.fromPoints([]);
        expect(box.halfAxes).toEqual(Matrix3.ZERO);
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints correct scale', function() {
        var box = OrientedBoundingBox.fromPoints(positions);
        expect(box.halfAxes).toEqual(Matrix3.fromScale(new Cartesian3(2.0, 3.0, 4.0)));
        expect(box.center).toEqual(Cartesian3.ZERO);
    });

    it('fromPoints correct translation', function() {
        var translation = new Cartesian3(10.0, -20.0, 30.0);
        var points = translatePositions(positions, translation);
        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqual(Matrix3.fromScale(new Cartesian3(2.0, 3.0, 4.0)));
        expect(box.center).toEqual(translation);
    });

    it('fromPoints rotation about z', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;
        rotation[1] = -rotation[1];
        rotation[3] = -rotation[3];

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(3.0, 2.0, 4.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation about y', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Y, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;
        rotation[2] = -rotation[2];
        rotation[6] = -rotation[6];

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(4.0, 3.0, 2.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation about x', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_X, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;
        rotation[5] = -rotation[5];
        rotation[7] = -rotation[7];

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(2.0, 4.0, 3.0), new Matrix3()), CesiumMath.EPSILON15);
        expect(box.center).toEqualEpsilon(Cartesian3.ZERO, CesiumMath.EPSILON15);
    });

    it('fromPoints rotation and translation', function() {
        var result = rotatePositions(positions, Cartesian3.UNIT_Z, CesiumMath.PI_OVER_FOUR);
        var points = result.points;
        var rotation = result.rotation;
        rotation[1] = -rotation[1];
        rotation[3] = -rotation[3];

        var translation = new Cartesian3(-40.0, 20.0, -30.0);
        points = translatePositions(points, translation);

        var box = OrientedBoundingBox.fromPoints(points);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.multiplyByScale(rotation, new Cartesian3(3.0, 2.0, 4.0), new Matrix3()), CesiumMath.EPSILON14);
        expect(box.center).toEqualEpsilon(translation, CesiumMath.EPSILON15);
    });

    it('fromRectangle sets correct default ellipsoid', function() {
        var rectangle = new Rectangle(-0.9, -1.2, 0.5, 0.7);
        var box1 = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0);
        var box2 = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, Ellipsoid.WGS84);

        expect(box1.center).toEqualEpsilon(box2.center, CesiumMath.EPSILON15);

        expect(box1.halfAxes).toEqualEpsilon(box2.halfAxes, CesiumMath.EPSILON15);
    });

    it('fromRectangle sets correct default heights', function() {
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var box = OrientedBoundingBox.fromRectangle(rectangle, undefined, undefined, Ellipsoid.UNIT_SPHERE);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle throws without rectangle', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        expect(function() {
            OrientedBoundingBox.fromRectangle(undefined, 0.0, 0.0, ellipsoid);
        }).toThrowDeveloperError();
    });

    it('fromRectangle throws with invalid rectangles', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-1.0, 1.0, 1.0, -1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, 2.0, -1.0, 1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-4.0, -2.0, 4.0, 1.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-2.0, -2.0, 1.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-1.0, -2.0, 2.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(new Rectangle(-4.0, -1.0, 4.0, 2.0), 0.0, 0.0, ellipsoid); }).toThrowDeveloperError();
    });

    it('fromRectangle throws with non-revolution ellipsoids', function() {
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        expect(function() { return OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, new Ellipsoid(1.01, 1.00, 1.01)); }).toThrowDeveloperError();
        expect(function() { return OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, new Ellipsoid(1.00, 1.01, 1.01)); }).toThrowDeveloperError();
    });

    it('fromRectangle creates an OrientedBoundingBox without a result parameter', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var box = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, ellipsoid);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle creates an OrientedBoundingBox with a result parameter', function() {
        var ellipsoid = Ellipsoid.UNIT_SPHERE;
        var rectangle = new Rectangle(0.0, 0.0, 0.0, 0.0);
        var result = new OrientedBoundingBox();
        var box = OrientedBoundingBox.fromRectangle(rectangle, 0.0, 0.0, ellipsoid, result);
        expect(box).toBe(result);

        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);

        var rotScale = Matrix3.ZERO;
        expect(box.halfAxes).toEqualEpsilon(rotScale, CesiumMath.EPSILON15);
    });

    it('fromRectangle for rectangles with heights', function() {
        var d90 = CesiumMath.PI_OVER_TWO;

        var box;

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), 1.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), -1.0, -1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), -1.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), 0.0, 1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 2, 0, 0, 0, 2, 0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), -1.0, -1.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), -1.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0.5, 1, 0, 0, 0, 1, 0), CesiumMath.EPSILON15);

    });

    it('fromRectangle for rectangles that span over half the ellipsoid', function() {
        var d90 = CesiumMath.PI_OVER_TWO;
        var d180 = CesiumMath.PI;
        var d135 = (3.0 / 4.0) * CesiumMath.PI;
        var d45 = CesiumMath.PI_OVER_FOUR;
        var onePlusSqrtHalfDivTwo = (1.0 + Math.SQRT1_2) / 2.0;
        var oneMinusOnePlusSqrtHalfDivTwo = 1.0 - onePlusSqrtHalfDivTwo;
        var sqrtTwoMinusOneDivFour = (Math.SQRT2 - 1.0) / 4.0;
        var sqrtTwoPlusOneDivFour = (Math.SQRT2 + 1.0) / 4.0;
        var box;

        // Entire ellipsoid
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, -d90, d180, d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, 0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 1, 0, 0, 0, 1, 0), CesiumMath.EPSILON15);

        // 3/4s of longitude, full latitude
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d135, -d90, d135, d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(oneMinusOnePlusSqrtHalfDivTwo, 0, 0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, onePlusSqrtHalfDivTwo, 1, 0, 0, 0, 1, 0), CesiumMath.EPSILON15);

        // 3/4s of longitude, 1/2 of latitude centered at equator
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d135, -d45, d135, d45), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(oneMinusOnePlusSqrtHalfDivTwo, 0, 0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, onePlusSqrtHalfDivTwo, 1, 0, 0, 0, Math.SQRT1_2, 0), CesiumMath.EPSILON15);

        // 3/4s of longitude centered at IDL, 1/2 of latitude centered at equator
        box = OrientedBoundingBox.fromRectangle(new Rectangle(d180, -d45, d90, d45), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(sqrtTwoMinusOneDivFour, -sqrtTwoMinusOneDivFour, 0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(Math.SQRT1_2, 0, sqrtTwoPlusOneDivFour, Math.SQRT1_2, 0, -sqrtTwoPlusOneDivFour, 0, Math.SQRT1_2, 0.0), CesiumMath.EPSILON15);

        // Full longitude, 1/2 of latitude centered at equator
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, -d45, d180, d45), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, 0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 1, 1, 0, 0, 0, Math.SQRT1_2, 0), CesiumMath.EPSILON15);

        // Full longitude, 1/4 of latitude starting from north pole
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, d45, d180, d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, onePlusSqrtHalfDivTwo), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, Math.SQRT1_2, Math.SQRT1_2, 0, 0, 0, oneMinusOnePlusSqrtHalfDivTwo, 0), CesiumMath.EPSILON15);

        // Full longitude, 1/4 of latitude starting from south pole
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, -d90, d180, -d45), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, -onePlusSqrtHalfDivTwo), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, Math.SQRT1_2, Math.SQRT1_2, 0, 0, 0, oneMinusOnePlusSqrtHalfDivTwo, 0), CesiumMath.EPSILON15);

        // Cmpletely on north pole
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, d90, d180, d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, 1), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);

        // Completely on north pole 2
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d135, d90, d135, d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, 1), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);

        // Completely on south pole
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d180, -d90, d180, -d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, -1), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);

        // Completely on south pole 2
        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d135, -d90, d135, -d90), 0, 0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0, 0, -1), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0), CesiumMath.EPSILON15);
    });

    it('fromRectangle for interesting, degenerate, and edge-case rectangles', function() {
        var d45 = CesiumMath.PI_OVER_FOUR;
        var d30 = CesiumMath.PI_OVER_SIX;
        var d90 = CesiumMath.PI_OVER_TWO;
        var d135 = 3 * CesiumMath.PI_OVER_FOUR;
        var d180 = CesiumMath.PI;
        var sqrt3 = Math.sqrt(3.0);

        var box;

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, 0.0, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d180, 0.0, -d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d180, 0.0, d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-1.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, d90, 0.0, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.0, 1.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(Matrix3.ZERO, CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, 0.0, d180, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.5, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d30, d90, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.1875 * sqrt3, 0.0, 0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, -sqrt3/4, 5*sqrt3/16, 1, 0, 0, 0, 3/4, 5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, -d90, d90, d30), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.1875 * sqrt3, 0.0, -0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0, sqrt3/4, 5*sqrt3/16, 1, 0, 0, 0, 3/4, -5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d30, d180, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.1875 * sqrt3, 0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1, 0, 0, 0, -sqrt3/4, 5*sqrt3/16, 0, 3/4, 5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d90, d180, d30), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.0, 0.1875 * sqrt3, -0.1875), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(-1, 0, 0, 0, sqrt3/4, 5*sqrt3/16, 0, 3/4, -5/16), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d45, 0.0, d45, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3((1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5 * (1.0 - Math.SQRT1_2), Math.SQRT1_2, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(d135, 0.0, -d135, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(-(1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, -0.5 * (1.0 - Math.SQRT1_2), -Math.SQRT1_2, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d45, 0.0, d45), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3((1.0 + Math.SQRT1_2) / 2.0, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5 * (1.0 - Math.SQRT1_2), 0.0, 0.0, 0.0, 0.0, Math.SQRT1_2, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(-d90, 0.0, d90, 0.0), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0), CesiumMath.EPSILON15);

        box = OrientedBoundingBox.fromRectangle(new Rectangle(0.0, -d90, 0.0, d90), 0.0, 0.0, Ellipsoid.UNIT_SPHERE);
        expect(box.center).toEqualEpsilon(new Cartesian3(0.5, 0.0, 0.0), CesiumMath.EPSILON15);
        expect(box.halfAxes).toEqualEpsilon(new Matrix3(0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0), CesiumMath.EPSILON15);
    });

    var intersectPlaneTestCornersEdgesFaces = function(center, axes) {
        var SQRT1_2 = Math.pow(1.0 / 2.0, 1 / 2.0);
        var SQRT3_4 = Math.pow(3.0 / 4.0, 1 / 2.0);

        var box = new OrientedBoundingBox(center, Matrix3.multiplyByScalar(axes, 0.5, new Matrix3()));

        var planeNormXform = function(nx, ny, nz, dist) {
            var n = new Cartesian3(nx, ny, nz);
            var arb = new Cartesian3(357, 924, 258);
            var p0 = Cartesian3.normalize(n, new Cartesian3());
            Cartesian3.multiplyByScalar(p0, -dist, p0);
            var tang = Cartesian3.cross(n, arb, new Cartesian3());
            Cartesian3.normalize(tang, tang);
            var binorm = Cartesian3.cross(n, tang, new Cartesian3());
            Cartesian3.normalize(binorm, binorm);

            Matrix3.multiplyByVector(axes, p0, p0);
            Matrix3.multiplyByVector(axes, tang, tang);
            Matrix3.multiplyByVector(axes, binorm, binorm);
            Cartesian3.cross(tang, binorm, n);
            if (Cartesian3.magnitude(n) === 0) {
                return undefined;
            }
            Cartesian3.normalize(n, n);

            Cartesian3.add(p0, center, p0);
            var d = -Cartesian3.dot(p0, n);
            if (Math.abs(d) > 0.0001 && Cartesian3.magnitudeSquared(n) > 0.0001) {
                return new Plane(n, d);
            }
            return undefined;
        };

        var pl;

        // Tests against faces

        pl = planeNormXform(+1.0, +0.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, +0.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +0.0, +1.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +0.0, -1.0,  0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +0.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +0.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, +1.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, -1.0,  0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +0.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +0.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, +1.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +0.0, -1.0, -0.49999); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +0.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, +0.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +0.0, +1.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +0.0, -1.0, -0.50001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

        // Tests against edges

        pl = planeNormXform(+1.0, +1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, +0.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +0.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +0.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +0.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, +1.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, +1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+0.0, -1.0, -1.0,  SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +0.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, -1.0,  SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +0.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +0.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +0.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, +1.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, +1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+0.0, -1.0, -1.0, -SQRT1_2 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, +0.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +0.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +0.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +0.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, +1.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, +1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+0.0, -1.0, -1.0, -SQRT1_2 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

        // Tests against corners

        pl = planeNormXform(+1.0, +1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, +1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(+1.0, -1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, +1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, +1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }
        pl = planeNormXform(-1.0, -1.0, -1.0,  SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INSIDE); }

        pl = planeNormXform(+1.0, +1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, -1.0,  SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, +1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(+1.0, -1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, +1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, +1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }
        pl = planeNormXform(-1.0, -1.0, -1.0, -SQRT3_4 + 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.INTERSECTING); }

        pl = planeNormXform(+1.0, +1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, +1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(+1.0, -1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, +1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, +1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }
        pl = planeNormXform(-1.0, -1.0, -1.0, -SQRT3_4 - 0.00001); if (pl) { expect(box.intersectPlane(pl)).toEqual(Intersect.OUTSIDE); }

    };

    it('intersectPlane works with untransformed box', function() {
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.IDENTITY);
    });

    it('intersectPlane works with off-center box', function() {
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(1.0, 0.0, 0.0), Matrix3.IDENTITY);
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(0.7, -1.8, 12.0), Matrix3.IDENTITY);
    });

    it('intersectPlane works with rotated box', function() {
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO,
                Matrix3.fromQuaternion(Quaternion.fromAxisAngle(new Cartesian3(0.5, 1.5, -1.2), 1.2), new Matrix3()));
    });

    it('intersectPlane works with scaled box', function() {
        var m = new Matrix3();
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.4, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(0.0, 0.4, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.0, 20.6), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(1.5, 0.4, 0.0), m));
        intersectPlaneTestCornersEdgesFaces(Cartesian3.ZERO, Matrix3.fromScale(new Cartesian3(0.0, 0.0, 0.0), m));
    });

    it('intersectPlane works with this arbitrary box', function() {
        var m = Matrix3.fromScale(new Cartesian3(1.5, 80.4, 2.6), new Matrix3());
        var n = Matrix3.fromQuaternion(Quaternion.fromAxisAngle(new Cartesian3(0.5, 1.5, -1.2), 1.2), new Matrix3());
        Matrix3.multiply(m, n, n);
        intersectPlaneTestCornersEdgesFaces(new Cartesian3(-5.1, 0.0, 0.1), n);
    });

    it('intersectPlane fails without box parameter', function() {
        var plane = new Cartesian4(1.0, 0.0, 0.0, 0.0);
        expect(function() {
            OrientedBoundingBox.intersectPlane(undefined, plane);
        }).toThrowDeveloperError();
    });

    it('intersectPlane fails without plane parameter', function() {
        var box = new OrientedBoundingBox(Cartesian3.IDENTITY, Matrix3.ZERO);
        expect(function() {
            OrientedBoundingBox.intersectPlane(box, undefined);
        }).toThrowDeveloperError();
    });

    it('distanceSquaredTo', function() {
        var r0 = Matrix3.fromRotationZ(CesiumMath.toRadians(-45.0));
        var r1 = Matrix3.fromRotationY(CesiumMath.toRadians(45.0));

        var rotation = Matrix3.multiply(r1, r0, r0);
        var scale = new Cartesian3(2.0, 3.0, 4.0);
        var rotationScale = Matrix3.multiplyByScale(rotation, scale, rotation);

        var center = new Cartesian3(4.0, 3.0, 2.0);

        var obb = new OrientedBoundingBox(center, rotationScale);

        var halfAxes = obb.halfAxes;
        var xAxis = Matrix3.getColumn(halfAxes, 0, new Cartesian3());
        var yAxis = Matrix3.getColumn(halfAxes, 1, new Cartesian3());
        var zAxis = Matrix3.getColumn(halfAxes, 2, new Cartesian3());

        // from positive x direction
        var cartesian = Cartesian3.multiplyByScalar(xAxis, 2.0, new Cartesian3());
        Cartesian3.add(cartesian, center, cartesian);

        var d = Cartesian3.distance(cartesian, center) - scale.x;
        var expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from negative x direction
        Cartesian3.multiplyByScalar(xAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.x;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from positive y direction
        Cartesian3.multiplyByScalar(yAxis, 2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.y;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from negative y direction
        Cartesian3.multiplyByScalar(yAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.y;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from positive z direction
        Cartesian3.multiplyByScalar(zAxis, 2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.z;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from negative z direction
        Cartesian3.multiplyByScalar(zAxis, -2.0, cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - scale.z;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);

        // from corner point
        Cartesian3.add(xAxis, yAxis, cartesian);
        Cartesian3.add(zAxis, cartesian, cartesian);

        var cornerDistance = Cartesian3.magnitude(cartesian);
        Cartesian3.add(cartesian, center, cartesian);

        d = Cartesian3.distance(cartesian, center) - cornerDistance;
        expected = d * d;
        expect(obb.distanceSquaredTo(cartesian)).toEqualEpsilon(expected, CesiumMath.EPSILON10);
    });

    it('distanceSquaredTo throws without box', function() {
        expect(function() {
            OrientedBoundingBox.distanceSquaredTo(undefined, new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('distanceSquaredTo throws without cartesian', function() {
        expect(function() {
            OrientedBoundingBox.distanceSquaredTo(new OrientedBoundingBox(), undefined);
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances', function() {
        var r0 = Matrix3.fromRotationZ(CesiumMath.toRadians(-45.0));
        var r1 = Matrix3.fromRotationY(CesiumMath.toRadians(45.0));

        var rotation = Matrix3.multiply(r1, r0, r0);
        var scale = new Cartesian3(2.0, 3.0, 4.0);
        var rotationScale = Matrix3.multiplyByScale(rotation, scale, rotation);

        var center = new Cartesian3(4.0, 3.0, 2.0);

        var obb = new OrientedBoundingBox(center, rotationScale);

        var halfAxes = obb.halfAxes;
        var xAxis = Matrix3.getColumn(halfAxes, 0, new Cartesian3());
        var yAxis = Matrix3.getColumn(halfAxes, 1, new Cartesian3());
        var zAxis = Matrix3.getColumn(halfAxes, 2, new Cartesian3());

        // from x direction
        var position = Cartesian3.multiplyByScalar(xAxis, 2.0, new Cartesian3());
        Cartesian3.add(position, center, position);

        var direction = Cartesian3.negate(xAxis, new Cartesian3());
        Cartesian3.normalize(direction, direction);

        var d = Cartesian3.distance(position, center);
        var expectedNear = d - scale.x;
        var expectedFar = d + scale.x;

        var distances = obb.computePlaneDistances(position, direction);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from y direction
        Cartesian3.multiplyByScalar(yAxis, 2.0, position);
        Cartesian3.add(position, center, position);

        Cartesian3.negate(yAxis, direction);
        Cartesian3.normalize(direction, direction);

        d = Cartesian3.distance(position, center);
        expectedNear = d - scale.y;
        expectedFar = d + scale.y;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from z direction
        Cartesian3.multiplyByScalar(zAxis, 2.0, position);
        Cartesian3.add(position, center, position);

        Cartesian3.negate(zAxis, direction);
        Cartesian3.normalize(direction, direction);

        d = Cartesian3.distance(position, center);
        expectedNear = d - scale.z;
        expectedFar = d + scale.z;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);

        // from corner point
        Cartesian3.add(xAxis, yAxis, position);
        Cartesian3.add(zAxis, position, position);

        Cartesian3.negate(position, direction);
        Cartesian3.normalize(direction, direction);

        var cornerDistance = Cartesian3.magnitude(position);
        Cartesian3.add(position, center, position);

        d = Cartesian3.distance(position, center);
        expectedNear = d - cornerDistance;
        expectedFar = d + cornerDistance;

        obb.computePlaneDistances(position, direction, distances);
        expect(distances.start).toEqualEpsilon(expectedNear, CesiumMath.EPSILON14);
        expect(distances.stop).toEqualEpsilon(expectedFar, CesiumMath.EPSILON14);
    });

    it('computePlaneDistances throws without a box', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(undefined, new Cartesian3(), new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances throws without a position', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(new OrientedBoundingBox(), undefined, new Cartesian3());
        }).toThrowDeveloperError();
    });

    it('computePlaneDistances throws without a direction', function() {
        expect(function() {
            OrientedBoundingBox.computePlaneDistances(new OrientedBoundingBox(), new Cartesian3(), undefined);
        }).toThrowDeveloperError();
    });

    it('isOccluded', function() {
        var occluderSphere = new BoundingSphere(new Cartesian3(0, 0, -1.5), 0.5);
        var occluder = new Occluder(occluderSphere, Cartesian3.ZERO);

        var radius = 0.25 / Math.sqrt(2.0);
        var halfAxes = Matrix3.multiplyByScale(Matrix3.IDENTITY, new Cartesian3(radius, radius, radius), new Matrix3());
        var obb = new OrientedBoundingBox(new Cartesian3(0, 0, -2.75), halfAxes);
        expect(obb.isOccluded(occluder)).toEqual(true);

        occluderSphere = new BoundingSphere(new Cartesian3(0, 0, -2.75), 0.25);
        occluder = new Occluder(occluderSphere, Cartesian3.ZERO);

        radius = 0.5 / Math.sqrt(2.0);
        halfAxes = Matrix3.multiplyByScale(Matrix3.IDENTITY, new Cartesian3(radius, radius, radius), new Matrix3());
        obb = new OrientedBoundingBox(new Cartesian3(0, 0, -1.5), halfAxes);
        expect(obb.isOccluded(occluder)).toEqual(false);
    });

    it('isOccluded throws without a box', function() {
        expect(function() {
            OrientedBoundingBox.isOccluded(undefined, new Occluder(new BoundingSphere(), new Cartesian3()));
        }).toThrowDeveloperError();
    });

    it('isOccluded throws without a occluder', function() {
        expect(function() {
            OrientedBoundingBox.isOccluded(new OrientedBoundingBox(), undefined);
        }).toThrowDeveloperError();
    });

    it('clone without a result parameter', function() {
        var box = new OrientedBoundingBox();
        var result = OrientedBoundingBox.clone(box);
        expect(box).not.toBe(result);
        expect(box).toEqual(result);
        expect(box.clone()).toEqual(box);
    });

    it('clone with a result parameter', function() {
        var box = new OrientedBoundingBox();
        var box2 = new OrientedBoundingBox();
        var result = new OrientedBoundingBox();
        var returnedResult = OrientedBoundingBox.clone(box, result);
        expect(result).toBe(returnedResult);
        expect(box).not.toBe(result);
        expect(box).toEqual(result);
        expect(box.clone(box2)).toBe(box2);
        expect(box.clone(box2)).toEqual(box2);
    });

    it('clone undefined OBB with a result parameter', function() {
        var box = new OrientedBoundingBox();
        expect(OrientedBoundingBox.clone(undefined, box)).toBe(undefined);
    });

    it('clone undefined OBB without a result parameter', function() {
        expect(OrientedBoundingBox.clone(undefined)).toBe(undefined);
    });

    it('equals works in all cases', function() {
        var box = new OrientedBoundingBox();
        expect(box.equals(new OrientedBoundingBox())).toEqual(true);
        expect(box.equals(undefined)).toEqual(false);
    });

    createPackableSpecs(OrientedBoundingBox, new OrientedBoundingBox(new Cartesian3(1.0, 2.0, 3.0), Matrix3.IDENTITY), [1.0, 2.0, 3.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]);
});
